import api from "@/api";
import type { Result } from "@/types";
import { isString } from "@/utils";
import {
	Form,
	Upload,
	type UploadProps
} from "@arco-design/web-react";
import type { UploadItem } from "@arco-design/web-react/es/Upload";
import { useState } from "react";
import App from "../App";

interface ImageUploadProps {
	id?: string;
	value?: string | string[];
	onChange?: (value: string | string[]) => void;
	/** 图片数量限制 */
	limit?: number;
	/** 图片大小限制 */
	size?: number;
	/** 图片类型限制 */
	fileTypes?: string;
	beforeUpload?: (file: File) => Promise<boolean>;
	beforeRemove?: (file: UploadItem) => Promise<boolean>;
}

export default function ImageUpload({
	value,
	onChange,
	limit = 0,
	size = 5120,
	fileTypes = "image/png,image/jpeg,image/gif,image/bmp,image/svg+xml",
	beforeUpload,
	beforeRemove,
}: ImageUploadProps) {
	const [fileList, setFileList] = useState<UploadItem[]>(
		(isString(value) ? [value] : (value ?? [])).map((x, index) => ({
			uid: x,
			url: x,
			status: "done",
			name: x.split("/").pop() ?? `image${index}`,
		})),
	);

	const { message, modal } = App.useApp();

	const { disabled: formDisabled } = Form.useFormContext();

	const handleChange = (fileList: UploadItem[]) => {
		if (fileList.some((i) => i.status !== 'done')) return;

		setFileList(fileList);
		const doneUrls = fileList
			.map((x) => {
				if (x.response) {
					const uploadResult = x.response as Result<string>;
					if (uploadResult.success) {
						return uploadResult.model;
					}
					return null;
				}
				return x.url!;
			})
			.filter((x) => x !== null);

		if (limit > 1) onChange?.(doneUrls);
		else onChange?.(doneUrls[0]);
	};

	const handleRemove: UploadProps["onRemove"] = (file) => {
		const promises: Promise<boolean>[] = [];
		promises.push(
			new Promise<boolean>((resolve, reject) => {
				modal.confirm!({ content: "确定删除该图片？", onConfirm: () => resolve(true), onCancel: () => reject("取消删除") });
			}),
		);
		if (beforeRemove) {
			promises.push(
				new Promise<boolean>((resolve, reject) =>
					beforeRemove(file)
						.then((res) => resolve(res))
						.catch((err) => reject(err)),
				),
			);
		}

		return new Promise<boolean>((resolve, reject) =>
			Promise.all(promises)
				.then((results) => resolve(results.every((x) => x)))
				.catch((err: Error) => reject(err)),
		);
	};

	const handleBeforeUpload: UploadProps["beforeUpload"] = (file) => {
		const promises: Promise<boolean>[] = [];
		if (size) {
			promises.push(
				new Promise<boolean>((resolve, reject) => {
					if (file.size <= size * 1024) resolve(true);
					else
						reject(new Error(`图片大小不能超过${(size / 1024).toFixed(1)}MB`));
				}),
			);
		}
		if (beforeUpload) {
			promises.push(
				new Promise<boolean>((resolve, reject) =>
					beforeUpload(file)
						.then((res) => resolve(res))
						.catch((err) => reject(err)),
				),
			);
		}

		if (promises.length === 0) return true;

		return new Promise<boolean>((resolve, reject) =>
			Promise.all(promises)
				.then((results) => resolve(results.every((x) => x)))
				.catch((err: Error) => {
					message.error!(err.message);
					reject(err);
				}),
		);
	};

	const handlePaste = async (e: React.ClipboardEvent<HTMLDivElement>) => {
		e.stopPropagation();
		if (limit !== 0 && fileList.length >= limit) return;

		const items = e.clipboardData?.items;
		if (!items?.length) return;

		const files: File[] = [];
		for (let i = 0; i < items.length; i++) {
			const element = items[i];
			if (element.type.indexOf("image") >= 0) {
				const file = element.getAsFile();
				if (file) files.push(file);
			}
		}
		if (files.length === 0) return;

		const formData = new FormData();
		for (const file of files) {
			formData.append("files[]", file, file.name);
		}

		const fileData = {};
		for (let i = 0; i < files.length; i++) {
			fileData[`file${i}`] = files[i];
		}
		const res = await api.baseUploadBatchCreate(fileData);
		setFileList([
			...fileList,
			...res.map(
				(x) =>
					({
						uid: x,
						url: x,
						name: x.split("/").pop() ?? x,
						status: "done",
					}) satisfies UploadItem,
			),
		]);
		if (limit > 1)
			onChange?.(
				fileList
					.filter((x) => x.status === "done")
					.map((x) => x.url!)
					.concat(res),
			);
		else onChange?.(res);
	};

	return (
		<div onPaste={handlePaste}>
			<Upload
				multiple={limit > 1}
				limit={limit}
				action="/api/base/upload"
				listType="picture-card"
				fileList={fileList}
				accept={fileTypes}
				disabled={formDisabled}
				onChange={handleChange}
				beforeUpload={handleBeforeUpload}
				onRemove={handleRemove}
			/>
			<span className="block text-[10px] text-[#999]">
				支持拖拽上传和粘贴上传
				{size && <>，图片大小不能超过{size / 1024}MB</>}
			</span>
		</div>
	);
}
